ffi: add new methods and error codes#62762
Conversation
|
Review requested:
|
|
Great! Thank you. It looks like I won't need my own unsafe-pointer once node:ffi gets stabilized 🎉 😄 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #62762 +/- ##
==========================================
- Coverage 89.69% 89.68% -0.01%
==========================================
Files 706 706
Lines 218143 218400 +257
Branches 41730 41805 +75
==========================================
+ Hits 195655 195864 +209
- Misses 14401 14419 +18
- Partials 8087 8117 +30
🚀 New features to boost your workflow:
|
|
LGTM |
|
@meixg I don't think it is acually necessary. When using the function you already own the source ( |
927da3b to
3e3b1da
Compare
Signed-off-by: Paolo Insogna <paolo@cowtech.it>
3e3b1da to
1fee1dd
Compare
| if (Buffer::HasInstance(args[0])) { | ||
| ptr = reinterpret_cast<uintptr_t>(Buffer::Data(args[0])); | ||
| } else if (args[0]->IsArrayBuffer()) { |
There was a problem hiding this comment.
This is already covered by the IsArrayBufferView branch below
| if (Buffer::HasInstance(args[0])) { | |
| ptr = reinterpret_cast<uintptr_t>(Buffer::Data(args[0])); | |
| } else if (args[0]->IsArrayBuffer()) { | |
| if (args[0]->IsArrayBuffer()) { |
| std::memchr(*value, '\0', value.length()) != nullptr) { | ||
| env->ThrowTypeError((label + " must not contain null bytes").c_str()); | ||
| THROW_ERR_INVALID_ARG_VALUE( | ||
| env, (label + " must not contain null bytes").c_str()); |
There was a problem hiding this comment.
| env, (label + " must not contain null bytes").c_str()); | |
| env, "%s must not contain null bytes", label); |
Ideally, this would be applied to all other formatting operations too
| " must have either 'returns', 'return' or 'result' " | ||
| "property"; | ||
| env->ThrowTypeError(msg.c_str()); | ||
| THROW_ERR_INVALID_ARG_VALUE(env, msg.c_str()); |
There was a problem hiding this comment.
| THROW_ERR_INVALID_ARG_VALUE(env, msg.c_str()); | |
| THROW_ERR_INVALID_ARG_VALUE(env, msg); |
| " must have either 'parameters' or 'arguments' " | ||
| "property"; | ||
| env->ThrowTypeError(msg.c_str()); | ||
| THROW_ERR_INVALID_ARG_VALUE(env, msg.c_str()); |
There was a problem hiding this comment.
| THROW_ERR_INVALID_ARG_VALUE(env, msg.c_str()); | |
| THROW_ERR_INVALID_ARG_VALUE(env, msg); |
| std::string msg = | ||
| "Return value type of function " + name + " must be a string"; | ||
| env->ThrowTypeError(msg.c_str()); | ||
| THROW_ERR_INVALID_ARG_VALUE(env, msg.c_str()); |
There was a problem hiding this comment.
| THROW_ERR_INVALID_ARG_VALUE(env, msg.c_str()); | |
| THROW_ERR_INVALID_ARG_VALUE(env, msg); |
| ("Argument " + std::to_string(index) + | ||
| " must be a buffer, an ArrayBuffer, a string, or a bigint") | ||
| .c_str()); |
There was a problem hiding this comment.
| ("Argument " + std::to_string(index) + | |
| " must be a buffer, an ArrayBuffer, a string, or a bigint") | |
| .c_str()); | |
| "Argument %s" | |
| " must be a buffer, an ArrayBuffer, a string, or a bigint", index); |
| ("Argument " + std::to_string(index) + | ||
| " must be a non-negative pointer bigint") | ||
| .c_str()); |
There was a problem hiding this comment.
| ("Argument " + std::to_string(index) + | |
| " must be a non-negative pointer bigint") | |
| .c_str()); | |
| "Argument %s" | |
| " must be a non-negative pointer bigint", index); |
| ("Invalid ArrayBuffer backing store for argument " + | ||
| std::to_string(index)) | ||
| .c_str()); |
There was a problem hiding this comment.
| ("Invalid ArrayBuffer backing store for argument " + | |
| std::to_string(index)) | |
| .c_str()); | |
| "Invalid ArrayBuffer backing store for argument %s", | |
| index); |
| ("Invalid ArrayBufferView backing store for argument " + | ||
| std::to_string(index)) | ||
| .c_str()); |
There was a problem hiding this comment.
| ("Invalid ArrayBufferView backing store for argument " + | |
| std::to_string(index)) | |
| .c_str()); | |
| "Invalid ArrayBufferView backing store for argument %s", index); |
| if (Buffer::HasInstance(args[0])) { | ||
| source_data = reinterpret_cast<uint8_t*>(Buffer::Data(args[0])); | ||
| source_len = Buffer::Length(args[0]); | ||
| } else if (args[0]->IsArrayBuffer()) { |
There was a problem hiding this comment.
ditto here
| if (Buffer::HasInstance(args[0])) { | |
| source_data = reinterpret_cast<uint8_t*>(Buffer::Data(args[0])); | |
| source_len = Buffer::Length(args[0]); | |
| } else if (args[0]->IsArrayBuffer()) { | |
| if (args[0]->IsArrayBuffer()) { |
There was a problem hiding this comment.
And more broadly than that, you can probably just implement this a lot more easily by using ArrayBufferViewContents
| return; | ||
| } | ||
| library_path = *filename; | ||
| lib->path_ = std::string(*filename); |
There was a problem hiding this comment.
| lib->path_ = std::string(*filename); | |
| lib->path_ = filename.ToString(); |
| if (ThrowIfContainsNullBytes(env, filename, "Library path")) { | ||
| return; | ||
| } | ||
| library_path = *filename; |
There was a problem hiding this comment.
This is a use-after-free bug – *filename goes out of scope outside of this conditional
| env->ThrowRangeError( | ||
| (std::string("The ") + label + " is too large").c_str()); | ||
| THROW_ERR_OUT_OF_RANGE( | ||
| env, (std::string("The ") + label + " is too large").c_str()); |
There was a problem hiding this comment.
| env, (std::string("The ") + label + " is too large").c_str()); | |
| "The %s is too large", label); |
| (std::string("The ") + label + " exceeds the platform pointer range") | ||
| .c_str()); |
There was a problem hiding this comment.
| (std::string("The ") + label + " exceeds the platform pointer range") | |
| .c_str()); | |
| "The %s exceeds the platform pointer range", label); |
| if (Buffer::HasInstance(args[0])) { | ||
| source_data = reinterpret_cast<uint8_t*>(Buffer::Data(args[0])); | ||
| source_len = Buffer::Length(args[0]); | ||
| } else if (args[0]->IsArrayBuffer()) { |
There was a problem hiding this comment.
And more broadly than that, you can probably just implement this a lot more easily by using ArrayBufferViewContents
|
@addaleax I think I addressed all your suggestions. Do you mind checking it again? |
|
|
||
| <a id="ERR_FFI_SYSCALL_FAILED"></a> | ||
|
|
||
| ### `ERR_FFI_SYSCALL_FAILED` |
There was a problem hiding this comment.
I'd say this is a bad name unless it actually has anything to do with syscalls, which is a pretty specific pre-existing term
There was a problem hiding this comment.
Indeed, I made other comments assuming that FFI_SYSCALL, though poorly named, should be taken to be something more like FFI_CALL, but instead it seems like it's being used as a more generic error.
There was a problem hiding this comment.
I couldn't find another example in errors.md, but maybe ERR_FFI_ERROR is generic enough? Otherwise, being specific for each of the error cases might be in order.
| THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBufferView backing store"); | ||
| return; | ||
| } | ||
| source_data = const_cast<uint8_t*>(view.data()); |
There was a problem hiding this comment.
This const_cast should not be necessary if source_data is a const uint8_t* (which it should be anyway)
| return; | ||
| } | ||
|
|
||
| uintptr_t ptr = 0; |
There was a problem hiding this comment.
This code makes the assumption that a BackingStore will never move even if no references are held by embedders; I am not sure if this is something that V8 guarantees, or something that we should rely on.
We can add this as experimental but we really, really need to evaluate whether this is an acceptable model that is guaranteed to be usable in the long term or not
| if (uv_dlsym(&lib_, name.c_str(), &ptr) != 0) { | ||
| std::string msg = std::string("dlsym failed: ") + uv_dlerror(&lib_); | ||
| env->ThrowError(msg.c_str()); | ||
| THROW_ERR_FFI_SYSCALL_FAILED(env, msg.c_str()); |
There was a problem hiding this comment.
This is not an FFI call that's failing, but the resolution of a symbol.
| } | ||
|
|
||
| env->ThrowError(msg); | ||
| THROW_ERR_FFI_SYSCALL_FAILED(env, msg); |
There was a problem hiding this comment.
This is also not an FFI call that's failing.
benchmark/ffi/getpid.jsusingffi.dlopen(null, ...)+uv_os_getpid. Thanks to @bengl for Foreign Function Interface (FFI) implementation #46905doc/api/errors.mdas asked from @jasnell in lib,src,test,doc: add node:ffi module #62072 (comment)As promised to @justjake, also the following followups requested in #62072 (comment):
ffi.exportArrayBuffer(...)andffi.exportArrayBufferView(...).exportBytes) to avoid extra JS Buffer wrapper allocations.